/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package persistencia;

import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javabeans.EnumTipoProyeccion.TipoProyeccion;
import javabeans.JBCine;
import javabeans.JBCliente;
import javabeans.JBFuncion;
import javabeans.JBPelicula;
import javabeans.JBSala;
import javax.servlet.ServletContext;

/**
 *
 * @author Administrador
 */
public class Sistema implements ISistema {

    // <editor-fold defaultstate="collapsed" desc="private atributes">
    private Connection connection;
    private static Sistema instance = null;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="constructor">
    public static Sistema getInstance(ServletContext context) {
        if (instance == null) {
            instance = new Sistema(context);
        }
        return instance;
    }

    private Sistema(ServletContext context) {
        String conn = context.getInitParameter("connStr");
        String user = context.getInitParameter("user");
        String pass = context.getInitParameter("password");
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection(conn, user, pass);

        } catch (SQLException ex) {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(Sistema.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="private methods">
    private int selectLasIdFromDB(String table) throws SQLException {
        int id = 0;
        try {
            Statement st = connection.createStatement();
            //PreparedStatement ps = connection.prepareStatement("SELECT LAST_INSERT_ID() as id");
            ResultSet rs = st.executeQuery("SELECT id from " + table + " ORDER BY id DESC LIMIT 1");
            if (rs.next()) {
                id = rs.getInt("id");
            }
        } catch (SQLException exc) {
            throw exc;
        }
        return id;
    }

    private List<JBFuncion> obtenerFuncionesPorPelicula(int idPelicula) {
        List<JBFuncion> funciones = new LinkedList<JBFuncion>();

        try {
            PreparedStatement psFuncion = connection.prepareStatement("SELECT * FROM funciones WHERE peliculaId = ?");
            psFuncion.setInt(1, idPelicula);
            ResultSet rs = psFuncion.executeQuery();
            while (rs.next()) {
                JBFuncion f = new JBFuncion();
                f.setId(rs.getInt("id"));
                f.setFecha(rs.getDate("fecha"));
                f.setDuracion(rs.getInt("duracion"));
                f.setCapacidad(rs.getInt("entradasDisponibles"));
                f.setSala(obtenerSala(rs.getInt("salaId")));
                f.setCine(obtenerCine(rs.getInt("cineid")));
                funciones.add(f);
            }
            psFuncion.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return funciones;
    }

    private List<JBFuncion> obtenerFuncionesPorCineySala(int idCine, int idSala) {
        List<JBFuncion> funciones = new LinkedList<JBFuncion>();

        try {
            PreparedStatement psFuncion = connection.prepareStatement("SELECT * FROM funciones WHERE cineId = ? AND salaId = ?");
            psFuncion.setInt(1, idCine);
            psFuncion.setInt(2, idSala);
            ResultSet rs = psFuncion.executeQuery();
            while (rs.next()) {
                JBFuncion f = new JBFuncion();
                f.setId(rs.getInt("id"));
                f.setFecha(rs.getDate("fecha"));
                f.setDuracion(rs.getInt("duracion"));
                f.setCapacidad(rs.getInt("entradasDisponibles"));
                f.setSala(obtenerSala(rs.getInt("salaId")));
                f.setCine(obtenerCine(rs.getInt("cineId")));
                funciones.add(f);
            }
            psFuncion.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return funciones;
    }

    private List<JBSala> obtenerSalas(int cineId) {
        List<JBSala> salas = new LinkedList<JBSala>();

        try {
            PreparedStatement psSala = connection.prepareStatement("SELECT * FROM salas WHERE cineId = ?");
            psSala.setInt(1, cineId);
            ResultSet rs = psSala.executeQuery();
            while (rs.next()) {
                JBSala sala = new JBSala();
                sala.setId(rs.getInt("id"));
                sala.setCineId(rs.getInt("cineId"));
                sala.setNumero(rs.getInt("numero"));
                sala.setCapacidad(rs.getInt("capacidad"));
                int proy = rs.getInt("proyeccion");
                if (proy == TipoProyeccion.proyeccion2D.ordinal()) {
                    sala.setTipoProyeccion(TipoProyeccion.proyeccion2D);
                } else {
                    sala.setTipoProyeccion(TipoProyeccion.proyeccion3D);
                }
                salas.add(sala);
            }
            psSala.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return salas;
    }

    private JBSala obtenerSala(int salaId) {
        JBSala sala = new JBSala();

        try {
            PreparedStatement psSala = connection.prepareStatement("SELECT * FROM salas WHERE id = ?");
            psSala.setInt(1, salaId);
            ResultSet rs = psSala.executeQuery();
            if (rs.next()) {
                sala.setId(rs.getInt("id"));
                sala.setCineId(rs.getInt("cineId"));
                sala.setNumero(rs.getInt("numero"));
                sala.setCapacidad(rs.getInt("capacidad"));
                int proy = rs.getInt("proyeccion");
                if (proy == TipoProyeccion.proyeccion2D.ordinal()) {
                    sala.setTipoProyeccion(TipoProyeccion.proyeccion2D);
                } else {
                    sala.setTipoProyeccion(TipoProyeccion.proyeccion3D);
                }
            }
            psSala.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return sala;
    }

    private Date fechaFinalizacion(Date fecha, int duracion) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(fecha);
        int horas = (int) duracion / 60;
        int minutos = duracion % 60;
        calendar.add(Calendar.HOUR, horas);
        calendar.add(Calendar.MINUTE, minutos);
        return calendar.getTime();
    }

    private boolean chequearFuncionesSolapadas(JBFuncion funcion) {
        List<JBFuncion> funciones = obtenerFuncionesPorCineySala(funcion.getCine().getId(), funcion.getSala().getId());
        if (funciones != null && !funciones.isEmpty()) {
            for (JBFuncion f : funciones) {
                if (f.getCine().getId() == funcion.getCine().getId()
                        && f.getSala().getId() == funcion.getSala().getId()) {
                    Date fechaFinActualF = fechaFinalizacion(f.getFecha(), f.getDuracion());
                    Date fechaFinFuncion = fechaFinalizacion(funcion.getFecha(), funcion.getDuracion());
                    if (fechaFinFuncion.after(f.getFecha()) || funcion.getFecha().before(fechaFinActualF)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="public methods">
    @Override
    public Date checkAndConvertDate(String dia, String mes, String anio) throws ParseException {
        Date fecha;
        DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
        try {
            int diaInt = Integer.valueOf(dia);
            int mesInt = Integer.valueOf(mes);
            int anioInt = Integer.valueOf(anio);
            String aux = diaInt + "/" + mesInt + "/" + anioInt;
            fecha = df.parse(aux);
            if (!df.format(fecha).equals(aux)) {
                fecha = null;
            }
        } catch (ParseException exc) {
            throw exc;
        }
        return fecha;
    }

    @Override
    public Date StringToDate(String fechaString, int horas, int minutos) throws ParseException {
        Date fecha;
        DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
        try {
            fecha = df.parse(fechaString);
            if (!df.format(fecha).equals(fechaString)) {
                fecha = null;
            } else {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(fecha);
                calendar.add(Calendar.HOUR, horas);
                calendar.add(Calendar.MINUTE, minutos);
                fecha = calendar.getTime();
            }

        } catch (ParseException exc) {
            throw exc;
        }
        return fecha;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="BD Methods">
    // <editor-fold defaultstate="collapsed" desc="Peliculas, Funciones, Cines y Salas">
    @Override
    public int salvarPelicula(JBPelicula pelicula) throws SQLException {
        int result = 0;
        try {
            PreparedStatement ps = connection.prepareStatement("INSERT INTO peliculas (nombre, fechaEstreno, proyeccion, idioma) "
                    + " VALUES (?,?,?,?)");
            ps.setString(1, pelicula.getNombre());
            ps.setDate(2, new java.sql.Date(pelicula.getFechaEstreno().getTime()));
            ps.setInt(3, pelicula.getTipoProyeccion().ordinal());
            ps.setInt(4, pelicula.getIdioma().ordinal());
            result = ps.executeUpdate();
            ps.close();

        } catch (SQLException exc) {
            throw exc;
        }
        return result;
    }

    @Override
    public int salvarFuncion(JBFuncion funcion, int peliculaId) throws SQLException {
        int resultado = 0;
        try {
            if (chequearFuncionesSolapadas(funcion)) {
                PreparedStatement ps = connection.prepareStatement("INSERT INTO funciones (fecha, duracion, peliculaId, salaId, cineId, entradasDisponibles) "
                        + "VALUES (?,?,?,?,?,?)");
                ps.setDate(1, new java.sql.Date(funcion.getFecha().getTime()));
                ps.setInt(2, funcion.getDuracion());
                ps.setInt(3, peliculaId);
                ps.setInt(4, funcion.getSala().getId());
                ps.setInt(5, funcion.getCine().getId());
                ps.setInt(6, funcion.getDuracion());
                resultado = ps.executeUpdate();
                ps.close();
            }
            return resultado;
        } catch (SQLException exc) {
            throw exc;
        }
    }

    @Override
    public List<JBPelicula> listarPeliculas() throws SQLException {
        List<JBPelicula> peliculas = new LinkedList<JBPelicula>();

        try {
            Statement st = connection.createStatement();
            ResultSet rs = st.executeQuery("SELECT * FROM peliculas");
            while (rs.next()) {
                JBPelicula p = new JBPelicula();
                p.setId(rs.getInt("id"));
                p.setNombre(rs.getString("nombre"));
                p.setFechaEstreno(rs.getDate("fechaEstreno"));
                p.setTipoProyeccion(rs.getInt("proyeccion"));
                p.setIdioma(rs.getInt("idioma"));
                peliculas.add(p);
            }
            st.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return peliculas;
    }
    
    @Override
    public List<JBPelicula> listarPeliculasxCine(int cineId) throws SQLException {
        List<JBPelicula> peliculas = new LinkedList<JBPelicula>();

        try {
            PreparedStatement ps = connection.prepareStatement("SELECT * FROM peliculas p INNER JOIN funciones f ON p.id = f.peliculaId WHERE cineId = ?");
            ps.setInt(1, cineId);
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                JBPelicula p = new JBPelicula();
                p.setId(rs.getInt("id"));
                p.setNombre(rs.getString("nombre"));
                p.setFechaEstreno(rs.getDate("fechaEstreno"));
                p.setTipoProyeccion(rs.getInt("proyeccion"));
                p.setIdioma(rs.getInt("idioma"));
                peliculas.add(p);
            }
            ps.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return peliculas;
    }

    @Override
    public JBPelicula obtenerPelicula(int id) throws SQLException {
        JBPelicula p = new JBPelicula();
        try {
            PreparedStatement ps = connection.prepareStatement("SELECT * FROM peliculas WHERE id = ?");
            ps.setInt(1, id);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                p.setId(rs.getInt("id"));
                p.setNombre(rs.getString("nombre"));
                p.setFechaEstreno(rs.getDate("fechaEstreno"));
                p.setTipoProyeccion(rs.getInt("proyeccion"));
                p.setIdioma(rs.getInt("idioma"));
                p.setListaFunciones(obtenerFuncionesPorPelicula(id));
            }
            ps.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return p;
    }

    @Override
    public List<JBCine> listarCines() throws SQLException {
        List<JBCine> cines = new LinkedList<JBCine>();

        try {
            Statement st = connection.createStatement();
            ResultSet rs = st.executeQuery("SELECT * FROM cines");
            while (rs.next()) {
                JBCine cine = new JBCine();
                cine.setId(rs.getInt("id"));
                cine.setNombre(rs.getString("nombre"));
                cine.setDireccion(rs.getString("direccion"));
                cine.setCiudad(rs.getString("ciudad"));
                cine.setEncargado(rs.getString("encargado"));
                cine.setSalas(obtenerSalas(cine.getId()));
                cines.add(cine);
            }
            st.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return cines;
    }

    @Override
    public JBCine obtenerCine(int cineId) throws SQLException {
        JBCine cine = new JBCine();

        try {
            PreparedStatement psCine = connection.prepareStatement("SELECT * FROM cines WHERE id = ?");
            psCine.setInt(1, cineId);
            ResultSet rs = psCine.executeQuery();
            if (rs.next()) {
                cine.setId(rs.getInt("id"));
                cine.setNombre(rs.getString("nombre"));
                cine.setDireccion(rs.getString("direccion"));
                cine.setCiudad(rs.getString("ciudad"));
                cine.setEncargado(rs.getString("encargado"));
                cine.setSalas(obtenerSalas(cine.getId()));
            }
            psCine.close();
            rs.close();
        } catch (SQLException exc) {
            exc.toString();
        }
        return cine;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Clientes y Usuarios">
    public JBCliente crearCliente(JBCliente cliente) throws SQLException {

        try {
            PreparedStatement psCliente = connection.prepareStatement("INSERT INTO clientes (ci, nombre, nacimiento, email)VALUES (?,?,?,?)");

            psCliente.setInt(1, cliente.getCi());
            psCliente.setString(2, cliente.getNombre());
            psCliente.setDate(3, new java.sql.Date(cliente.getFechaNacimiento().getTime()));
            psCliente.setString(4, cliente.getEmail());

            int result = psCliente.executeUpdate();

            psCliente.close();

            if (result > 0) {
                PreparedStatement psUsuario = connection.prepareStatement("INSERT INTO usuarios (ci, usuario, password, tipousuario)VALUES (?,?,?,?)");

                psUsuario.setInt(1, cliente.getCi());
                psUsuario.setString(2, cliente.getUsuario().getUsuario());
                psUsuario.setString(3, cliente.getUsuario().getPassword());
                psUsuario.setString(4, cliente.getUsuario().getTipoUsuario());

                int resultUsuario = psUsuario.executeUpdate();

                if (resultUsuario > 0) {
                    return cliente;
                }
            }
        } catch (SQLException exc) {
            throw exc;
        }
        return null;
    }
    // </editor-fold>
    // </editor-fold>
}
